package at.pp.match3.game;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Typeface;
import at.pp.match3.R;
import at.pp.match3.core.LOG;
import at.pp.match3.game.assets.TextAsset;
import at.pp.match3.game.assets.TileAsset;
import at.pp.match3.game.render.GameAsset;

public class GameEngine extends AbstractEngine {

    private static final int NUMBER_OF_TILES = 5;
    private static final int BACKGROUND_COLOR = Color.rgb(183, 247, 231);
    private static int TILE_SIZE = 50;
    private static final int GRID_SIZE_X = 4;
    private static final int GRID_SIZE_Y = 8;
    private static final long ONE_SECOND = 1000;
    private static final int SPLASHSCREEN_TIME = 1;
    private static final int LEVEL_ONE_TIME = 60;
    private static final String REMAINING_TIME = "Time: ";
    private Bitmap redTile;
    private Bitmap yellowTile;
    private Bitmap purpleTile;
    private Bitmap blueTile;
    private Bitmap greenTile;

    private int currentSplashScreenTime;
    private int currentLevelTime;

    private GameAsset selectedTile;
    private TextAsset timeText;

    private TimerTask timerTask;
    private final Timer timer;
    private Bitmap splash;
    private Bitmap lose;
    private boolean lateAssetsLoaded;

    protected List<GameAsset> gameAssets = new ArrayList<GameAsset>();
    protected List<GameAsset> menuAssets = new ArrayList<GameAsset>();

    public GameEngine(Context context) {
        super(context);
        currentSplashScreenTime = SPLASHSCREEN_TIME;
        currentLevelTime = LEVEL_ONE_TIME;
        timer = new Timer();
        setGameState(GameState.Splash);
        startSplashScreenTimer();
    }

    @Override
    public void updatePhysics() {
        // Log.d(TAG, "updatePhysics");
    }

    @Override
    public final void renderScene(final Canvas canvas) {
        if (gameState == GameState.Splash) {
            canvas.drawBitmap(splash, 0, 0, null);
        } else if (gameState == GameState.Running) {
            canvas.drawColor(BACKGROUND_COLOR);
            renderer.renderAssets(canvas, gameAssets);
        } else if (gameState == GameState.Menu) {
            canvas.drawColor(Color.DKGRAY);
            renderer.renderAssets(canvas, menuAssets);

            if (menuAssets.get(0).isSelected()) {
                gameState = GameState.Running;
                timerTask = null;
                currentLevelTime = LEVEL_ONE_TIME;
                timeText.setText(REMAINING_TIME + currentLevelTime);
                startLevelTimer();
            }
        } else if (gameState == GameState.Lose) {
            canvas.drawColor(Color.RED);
            canvas.drawBitmap(lose, 0, 0, null);
        }
    }

    @Override
    public final void loadAssets() {
        lateAssetsLoaded = false;

        createMenu();
        createTimeText();
        createGrid();
        loadLoseScreen();

        lateAssetsLoaded = true;
    }

    private void loadLoseScreen() {
        lose = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.lose);
    }

    private void createMenu() {
        menuAssets.clear();
        Bitmap startButtonBitmap = BitmapFactory.decodeResource(
                context.getResources(), R.drawable.button_start);
        GameAsset startButton = new TileAsset(0, 0, startButtonBitmap);
        startButton.setSelectBitmap(startButtonBitmap);
        menuAssets.add(startButton);
    }

    private void createSplash(Resources resources) {
        splash = BitmapFactory.decodeResource(resources, R.drawable.splash);
    }

    private void startSplashScreenTimer() {
        timerTask = new TimerTask() {
            @Override
            public void run() {
                updateSplashScreenTime();
            }
        };
        timer.schedule(timerTask, ONE_SECOND);
    }

    private void updateSplashScreenTime() {
        currentSplashScreenTime = currentSplashScreenTime - 1;
        if (currentSplashScreenTime <= 0 && lateAssetsLoaded) {
            gameState = GameState.Menu;
        } else {
            startSplashScreenTimer();
        }
    }

    private void startLevelTimer() {
        timerTask = new TimerTask() {
            @Override
            public void run() {
                updateLevelTime();
            }
        };
        timer.schedule(timerTask, ONE_SECOND);
    }

    private void updateLevelTime() {
        currentLevelTime = currentLevelTime - 1;
        timeText.setText(REMAINING_TIME + currentLevelTime);
        if (currentLevelTime <= 0) {
            gameState = GameState.Lose;
        } else {
            startLevelTimer();
        }
    }

    private void createTimeText() {
        timeText = new TextAsset(20, 20);
        timeText.setText(REMAINING_TIME + currentLevelTime);
        AssetManager assets = this.context.getAssets();
        Typeface typeface = Typeface.createFromAsset(assets, "fonts/font.ttf");
        timeText.setTypeFace(typeface);
        gameAssets.add(timeText);
    }

    private void createGrid() {
        // gameAssets.clear();
        Resources resources = context.getResources();
        redTile = BitmapFactory.decodeResource(resources, R.drawable.dot_red);
        yellowTile = BitmapFactory.decodeResource(resources,
                R.drawable.dot_yellow);
        purpleTile = BitmapFactory.decodeResource(resources,
                R.drawable.dot_purple);
        greenTile = BitmapFactory.decodeResource(resources,
                R.drawable.dot_green);
        blueTile = BitmapFactory.decodeResource(resources, R.drawable.dot_blue);

        TILE_SIZE = blueTile.getHeight();

        // compute space for the sides to place the grid directly in the middle
        int spaceLeft = (surfaceSizeWidth - TILE_SIZE * GRID_SIZE_X) / 2;
        int spaceTop = (surfaceSizeHeight - TILE_SIZE * GRID_SIZE_Y) / 2;

        LOG.log("surfaceSizeWidth: " + surfaceSizeWidth);
        LOG.log("surfaceSizeHeight: " + surfaceSizeHeight);
        LOG.log("spaceLeft: " + spaceLeft);
        LOG.log("spaceTop: " + spaceTop);

        LOG.log("tileSize: " + blueTile.getHeight());

        Random random = new Random();
        Bitmap bitmap;

        for (int i = 0; i < GRID_SIZE_X; i++) {
            for (int j = 0; j < GRID_SIZE_Y; j++) {

                switch (random.nextInt(NUMBER_OF_TILES)) {
                case 0:
                    bitmap = redTile;
                    break;

                case 1:
                    bitmap = yellowTile;
                    break;

                case 2:
                    bitmap = purpleTile;
                    break;

                case 3:
                    bitmap = greenTile;
                    break;

                case 4:
                    bitmap = blueTile;
                    break;

                default:
                    bitmap = redTile;
                    break;
                }

                GameAsset asset = new TileAsset(spaceLeft + (TILE_SIZE * i),
                        spaceTop + (TILE_SIZE * j), bitmap);
                asset.setSelectBitmap(redTile);
                gameAssets.add(asset);
            }
        }
    }

    @Override
    public final void processMotionEvent() {
        if (isTouching || isMoving) {
            selectAsset();
        }
    }

    private void unselectAsset() {
        if (selectedTile != null) {
            selectedTile.select(false);
            selectedTile = null;
        }
    }

    private void selectAsset() {
        boolean assetSelected = false;

        List<GameAsset> allAssets = new ArrayList<GameAsset>(gameAssets);
        allAssets.addAll(menuAssets);
        for (GameAsset asset : allAssets) {
            if (asset.isSelecatable()
                    && asset.getSelectionRect().contains(currentTouchX,
                            currentTouchY)) {
                unselectAsset();
                asset.select(true);
                selectedTile = asset;
                assetSelected = true;
            }
        }
        if (!assetSelected) {
            unselectAsset();
            currentLevelTime = 5;
        }
    }

    @Override
    public void loadEarlyAssets() {
        createSplash(context.getResources());
    }

}
